
#include "Class.h"

#include "Repl.h"
#include "Parser.h"
#include "CodeMethod.h"
#include <readline/readline.h>
#include <readline/history.h>
#include <sstream>
#include <stdlib.h>
#include "util.h"
#include "PrimitiveMethod.h"
#include "Class.h"

Object* primitive_debug_request(Image* image,Object* self,int argc,Object** argv){
    Object* r=image->getClass("ReplDebugRequest")->newInstance();
    return r;
}
Object* primitive_class_request(Image* image,Object* self,int argc,Object** argv){
    if(argc!=1) image->exception("Repl.newClass requires one argument");
    Object* r=image->getClass("ReplClassRequest")->newInstance();
    r->set("class",argv[0]);
    return r;
}
Object* primitive_method_request(Image* image,Object* self,int argc,Object** argv){
    if(argc!=1) image->exception("Repl.methodIn: requires one argument");
    Object* r=image->getClass("ReplMethodRequest")->newInstance();
    r->set("class",argv[0]);
    return r;
}

void Repl::prepareImage(Image* image){
    Class* replClass=createClass(image,"Repl");
    new PrimitiveMethod(image,"newClass:",replClass,1,&primitive_class_request);
    new PrimitiveMethod(image,"methodIn:",replClass,1,&primitive_method_request);
    new PrimitiveMethod(image,"debug",replClass,0,&primitive_debug_request);
    Object* repl=replClass->newInstance();
    image->addObject("Repl",repl);

    Class* classRequest=createClass(image,"ReplClassRequest");
    //string name
    classRequest->addField("class",NULL,FIELD_ORDINARY);

    Class* methodRequest=createClass(image,"ReplMethodRequest");
    //Class class
    methodRequest->addField("class",NULL,FIELD_ORDINARY);

    Class* debugRequest=createClass(image,"ReplDebugRequest");
}
istream* Repl::askLine(string prompt){
    if(stream!=NULL){
        return stream;
    }else{
        istringstream* stream=new istringstream();
        char* r=readline(prompt.c_str());
        stream->str(r);
        free(r);
        return stream;
    }
}
void Repl::run(istream& stream){
    map<string,Object*> temps;
    temps["repl_echo"]=makeBoolean(image,false);
    temps["repl_value"]=makeBoolean(image,false);
    Repl::stream=&stream;
    //printf("KWIQOS Repl alpha in batch mode\n\n");
    while(!stream.eof()){
        statement(stream,&temps);
    }
    Repl::stream=NULL;
}
Repl::~Repl(){
    delete parser;
}
Repl::Repl(Image* image) {
    Repl::image=image;
    classRequest=image->getClass("ReplClassRequest");
    methodRequest=image->getClass("ReplMethodRequest");
    debugRequest=image->getClass("ReplDebugRequest");
    replClass=image->getClass("Repl");
    parser=new Parser(image);
}
void Repl::statement(istream& inputStream,map<string,Object*>* temps){
    Statement st=parser->parseStatement(inputStream);
    Expression* e=st.rvalue;
    if(inspectBoolean((*temps)["repl_echo"])){
        print_expr(image,e);printf("\n");
    }
    Object* res=execute_expr(e,image,NULL,temps);
    if(inspectBoolean((*temps)["repl_value"])){
        printf("    :");
        printObject(image,res);
        printf("\n");
    }
    if(Repl::stream!=NULL){
        parser->skipStatementRemnants(inputStream);
    }
    if(st.type==STATEMENT_ASSIGN){
        (*temps)[st.lvalue]=res;
    }
    handleRequest(res);
}
void Repl::run(){
    map<string,Object*> temps;
    temps["repl_echo"]=makeBoolean(image,false);
    temps["repl_value"]=makeBoolean(image,true);
    printf("KWIQOS Repl alpha\n\n");
    while(true){
        char *t=readline("KW>");
        if(t && strlen(t)>0){
            add_history(t);
            string input(t);
            istringstream inputStream;
            inputStream.str(input);
            statement(inputStream,&temps);
        }
        free(t);
    }
}
void Repl::handleRequest(Object* req){
    //printf("Is req %X ?\n",req);
    //printf("Is req %s ?\n",((Class*)req->getClass())->getName().c_str());
    if(req->isKindOf(methodRequest)){
        Object* cont=req->get("class");
        istream* stream=askLine("def>");
        addMethod(cont,*stream);
        if(Repl::stream==NULL) delete stream;
    }else if(req->isKindOf(classRequest)){
        string name=inspectString(req->get("class"));
        Class* nc=createClass(image,name);
        //printf("Created new class %s.\n",name.c_str());
    }else if(req->isKindOf(debugRequest)){
        printf("Debug request\n");
    }
}
void Repl::addMethod(Object* cont, istream& stream){
    CodeMethod* cm=parser->parseMethod(stream);
    Object* cmo=cm;
    parser->skipWhitespace(stream);
    cont->send(image,"addMethod:",1,&cmo);
}